// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.animation {
	import actionml.Global;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.Dictionary;

	public class RenderManager {
		private static var _renderListener:Sprite = new Sprite();
		private static var _rendering:Boolean = false;
		private static var _renderContext:RenderContext = new RenderContext();
		
		private static var _activeTargets:Number = 0;
		private static var _activeRenderers:Dictionary = new Dictionary();
		
		private static var _newRenderersForTargets:Dictionary = new Dictionary();
		private static var _removedRenderers:Dictionary = new Dictionary();
		private static var _finishedTargets:Array = [];
		
		
		public static function addBinding(target:Object, property:String, expression:Object) {
			RenderManager.addRenderer(new ExpressionRenderer(target, property, expression));
		}
		// if the target of the renderer changes the renderer must be removed and added back to 
		// the render manager if it is managed by the render manager
		public static function addRenderer(renderer:IRenderer) {
			var t:Object = RenderManager["getTracer"]({method:"addRenderer", context: "arg"});
			t.info("adding", renderer);
			if (!_activeRenderers[renderer.target]) {
				if (_activeTargets == 0) {
					_renderListener.addEventListener(Event.ENTER_FRAME, RenderManager.enterFrameHandler)
				}
				_activeTargets++;
				_activeRenderers[renderer.target] = [];
			} else {
				for each (var existingRenderer:IRendererInternal in _activeRenderers[renderer.target]) {
					if (renderer.name == existingRenderer.name) {
						existingRenderer.interrupted(renderer);
						RenderManager.removeRenderer(existingRenderer, true);
					}
				}
			}	
			_newRenderersForTargets[renderer.target] = true;
			_activeRenderers[renderer.target].push(renderer);
			IRendererInternal(renderer).prepareToRender(); 
		}
		public static function removeRenderer(renderer:IRenderer, interruption:Boolean=false) {
			var t:Object = RenderManager["getTracer"]({method:"addRenderer", context: "arg"});
			t.info("removing", renderer);
			renderer = IRendererInternal(renderer);
			if (_rendering) {
				if (!_removedRenderers[renderer.target]) {	
					_removedRenderers[renderer.target] = [];
				}
				_removedRenderers[renderer.target].push(renderer);
			} else {
				_activeRenderers[renderer.target].remove(renderer);
				if (_activeRenderers[renderer.target].length == 0 && !interruption) {
					_activeTargets--;
					delete _activeRenderers[renderer.target];	
				}
				if (_activeTargets == 0 && !interruption) {
					_renderListener.removeEventListener(Event.ENTER_FRAME, RenderManager.enterFrameHandler);
				}
			}
			IRendererInternal(renderer).stopRendering();
		}
		private static function enterFrameHandler(e:Event) {
			_rendering = true;
			_renderContext.initialize();
			_newRenderersForTargets = new Dictionary();
			for (var target:Object in _activeRenderers) {
				_renderContext.initializeTarget();
				var targetRenderers:Array = _activeRenderers[target];
				for each (var activeRenderer:IRendererInternal in targetRenderers) {
					activeRenderer.render(_renderContext);
					if (!activeRenderer.rendering) {
						RenderManager.removeRenderer(activeRenderer);
					}
				}
				_renderContext.postRenderTarget(target);
				for each (var renderer:IRenderer in _removedRenderers[target]) {
					targetRenderers.remove(renderer); // INLINE???
				}
				delete _removedRenderers[target];
				if (targetRenderers.length == 0) {
					if (!_finishedTargets.contains(target)) {
 						_finishedTargets.push(target);
					}
				}
			}
			_renderContext.postRenderAll();
			for each (var finishedTarget:Object in _finishedTargets) {
				if (!_newRenderersForTargets[finishedTarget]) {
					_activeTargets--;
					delete _activeRenderers[finishedTarget];
				}	
			}
			_finishedTargets = [];
			if (_activeTargets == 0) {
				_renderListener.removeEventListener(Event.ENTER_FRAME, RenderManager.enterFrameHandler);
			}
			_rendering = false;
		}
	}
}